package com.cs307.redcross.client;

import java.io.Serializable;
import java.util.Date;
import java.util.Vector;

import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.AppointmentStyle;
import com.bradrydzewski.gwt.calendar.client.Calendar;
import com.bradrydzewski.gwt.calendar.client.CalendarSettings;
import com.bradrydzewski.gwt.calendar.client.CalendarSettings.Click;
import com.bradrydzewski.gwt.calendar.client.CalendarViews;
import com.bradrydzewski.gwt.calendar.client.event.DeleteEvent;
import com.bradrydzewski.gwt.calendar.client.event.DeleteHandler;
import com.bradrydzewski.gwt.calendar.client.event.UpdateEvent;
import com.bradrydzewski.gwt.calendar.client.event.UpdateHandler;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.TabBar;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.datepicker.client.DatePicker;

public class CalendarContent extends Content implements Serializable {
	private DatabaseHandlerServiceAsync databaseHandlerService = (DatabaseHandlerServiceAsync) GWT.create(DatabaseHandlerService.class);
	
	private static final long serialVersionUID = 7554166675939825567L;
	
	private Calendar calendar;
	private CalendarSettings settings;
	private VerticalPanel mainPanel;
    private TabBar bar;
    private PopupPanel datePickerPopup;
    private DatePicker datePicker;
    private Label monthLabel;
    private int selectedView;
    
    private DialogBox eventPopup;
    private VerticalPanel eventPopupPanel;
    private SimpleEventsContent simpleEventsContent;
    private Button updateEventButton;
    private Button cancelUpdateButton;
    private HorizontalPanel buttonPanel;
    
    private Appointment tempEvent; //used to store an event which needs to be written to the database
    private Appointment currentAppointment; //Used to store appointments currently being updated
    
    private boolean viewAllEvents; //used for admins to toggle showing events for current users/ events for all users
    
    private Person p;
    
	public CalendarContent() {
		calendar = new Calendar();
		settings = new CalendarSettings();
		settings.setTimeBlockClickNumber(Click.Double);
		calendar.setSettings(settings);
		//calendar.setHeight("100%");
		//calendar.setWidth("100%");
		calendar.setPixelSize(Window.getClientWidth(), Window.getClientHeight());
		
		viewAllEvents = true;
		
		mainPanel = new VerticalPanel();
	    bar = new TabBar();
	    monthLabel = new Label("");
	    monthLabel.setHorizontalAlignment(Label.ALIGN_CENTER);
	    monthLabel.setStylePrimaryName("calendar_month");
	    
	    updateMonthLabel();
	    selectedView = -1;
	    
	    datePickerPopup = new PopupPanel(true);
//	    datePickerPopup.setText("Select Date");
//	    datePickerPopup.setModal(true);
	    datePicker = new DatePicker();
	    
	    datePickerPopup.add(datePicker);
	    datePickerPopup.setPopupPosition(bar.getAbsoluteLeft()+100, bar.getAbsoluteTop()+100);
	    
		initWidget(mainPanel);
	    
	 // Create tab bar.
	    bar.addTab("Day");
	    bar.addTab("Week");
	    bar.addTab("Month");
	    bar.addTab("Select");
	    bar.addTab("");
	    bar.addTab("Previous");
	    bar.addTab("Next");

	    
	    // Hook up a tab listener to do change calendar view
	    bar.addSelectionHandler(new SelectionHandler<Integer>() {
	      public void onSelection(SelectionEvent<Integer> event) {
	        switch(event.getSelectedItem()){
	        case(0):
	        	calendar.setView(CalendarViews.DAY, 1);
	        	selectedView = 0;
	        	break;
	        case(1):
	        	calendar.setView(CalendarViews.DAY, 7);
	        	selectedView = 1;
	        	break;
	        case(2):
	        	calendar.setView(CalendarViews.MONTH);
	        	selectedView = 2;
	        	break;
	        case(3):
	        	datePicker.setCurrentMonth(calendar.getDate());
	        	datePickerPopup.show();
	        	bar.selectTab(selectedView, false);
	        	break;
	        case(4):
	        	//Do nothing
	        	bar.selectTab(selectedView, false);
	        	break;
	        case(5):
	        	//Previous button
	        	switch(selectedView){	
	        	case(0):
	        		calendar.setDate(new Date(calendar.getDate().getTime()-(1000 * 60 * 60 * 24)));
	        		break;
	        	case(1):
	        		calendar.setDate(new Date(calendar.getDate().getTime()-(7 * 1000 * 60 * 60 * 24)));
	        		break;
	        	case(2):
	        		Date newDate = calendar.getDate();
	        		newDate.setMonth(newDate.getMonth()-1);
	        		calendar.setDate(newDate);
	        		break;
	        	default:
	        		calendar.setDate(new Date(calendar.getDate().getTime()-(3 * 1000 * 60 * 60 * 24)));
	        		break;
	        	}
	        	bar.selectTab(selectedView, false);
	        	updateMonthLabel();
	        	break;
	        case(6):
	        	//Next button
	        	switch(selectedView){	
	        	case(0):
	        		calendar.setDate(new Date(calendar.getDate().getTime()+(1000 * 60 * 60 * 24)));
	        		break;
	        	case(1):
	        		calendar.setDate(new Date(calendar.getDate().getTime()+(7 * 1000 * 60 * 60 * 24)));
	        		break;
	        	case(2):
	        		Date newDate = calendar.getDate();
	        		newDate.setMonth(newDate.getMonth()+1);
	        		calendar.setDate(newDate);
	        		break;
	        	default:
	        		calendar.setDate(new Date(calendar.getDate().getTime()+(3 * 1000 * 60 * 60 * 24)));
	        		break;
	        	}
	        	bar.selectTab(selectedView, false);
	        	updateMonthLabel();
	        	break;
	        case(7):
	        	bar.selectTab(selectedView, false);
	        	break;
	        case(8):
	        	if(viewAllEvents == false){
	        		bar.setTabText(8, "View My Events Only");
	        		viewAllEvents = true;
	        	}
	        	else{
	        		bar.setTabText(8, "View Events for All Users");
	        		viewAllEvents = false;
	        	}
	        	bar.selectTab(selectedView, false);
	        	updateCalendar();
	        	break;
	        }
	      }
	    });

	    // Add it to the root panel.
	    mainPanel.add(monthLabel);
	    mainPanel.add(bar);
		calendar.setDate(new Date()); //calendar date, not required
		calendar.setDays(3); //number of days displayed on startup
		mainPanel.add(calendar);
		
		// Populate calendar with events
		updateCalendar();
		
		//Change handler for the date picker popup
		datePicker.addValueChangeHandler(new ValueChangeHandler<Date>() {
		      public void onValueChange(ValueChangeEvent<Date> event) {
		        Date date = event.getValue();
		        calendar.setDate(date);		        
		        datePickerPopup.hide();
		        updateMonthLabel();
		      }
		});
		
//		//Blank area clicked handler, create new event
//		//Not implemented.  Scrolling within the browser window causes the incorrect time to be selected
//		calendar.addTimeBlockClickHandler(new TimeBlockClickHandler<Date>(){
//			  @Override
//			  public void onTimeBlockClick(TimeBlockClickEvent<Date> newDate) {
//			      //TODO Show create event popup
//				  Window.alert("New Event Requested at time " + newDate.getTarget().toString());
//			  }             
//		});
	
		
		p = Server.currentUser;
		if (p instanceof Administrator){
			bar.addTab("");
			bar.addTab("View My Events Only");
			viewAllEvents = true;
			//Only add event handlers (to modify calendar) if user is an admin
						
			//Existing event opened (double clicked) handler
			calendar.addOpenHandler(new OpenHandler<Appointment>(){
				@Override
				public void onOpen(OpenEvent<Appointment> appointment){
					currentAppointment = appointment.getTarget();
					databaseHandlerService.getEvent(Integer.parseInt(appointment.getTarget().getId()), new AsyncCallback<Event>() {
						
						@Override
						public void onFailure(Throwable caught) {
							Window.alert("Error: Database Failure.");
						}
	
						@Override
						public void onSuccess(Event result) {
							//TODO Show edit event popup when event is opened on calendar					
							eventPopup = new DialogBox(true);
							eventPopup.setModal(true);
							eventPopup.setText("Update Event");
							simpleEventsContent = new SimpleEventsContent();
							simpleEventsContent.populateForm(result);
							eventPopupPanel = new VerticalPanel();
							eventPopupPanel.add(simpleEventsContent);
							updateEventButton = new Button("Update Event");
							cancelUpdateButton = new Button("Cancel");
							buttonPanel = new HorizontalPanel();
							buttonPanel.add(updateEventButton);
							buttonPanel.add(cancelUpdateButton);
							
							updateEventButton.addClickHandler(new ClickHandler() {
								public void onClick(ClickEvent event) {
									Event newEvent = simpleEventsContent.saveEvent();
									if(event != null){
										currentAppointment.setId(Integer.toString(newEvent.getId()));
										currentAppointment.setTitle(newEvent.getName());
										currentAppointment.setDescription(newEvent.getDescription());
										currentAppointment.setStart(newEvent.getStartTime());
										currentAppointment.setEnd(newEvent.getEndTime());
										eventPopup.hide();
										calendar.doLayout();
									}
									else{
										//Event not written successfully
										//TODO display error message
										eventPopup.hide();
									}
								}
							});
							
							cancelUpdateButton.addClickHandler(new ClickHandler() {
								public void onClick(ClickEvent event) {
									eventPopup.hide();
								}
							});
							
							eventPopupPanel.add(buttonPanel);
							eventPopup.add(eventPopupPanel);
							eventPopup.center();
							eventPopup.show();
						}
							
					});
					
					
				}
			});
			
			//Existing event updated (drag and drop) handler
			calendar.addUpdateHandler(new UpdateHandler<Appointment>() {
				  @Override
				  public void onUpdate(UpdateEvent<Appointment> event) {
	//			    boolean commit = Window.confirm(
	//			                       "Are you sure you want to update the appointment \""
	//			                        + event.getTarget().getTitle() + "\"");
	//			    if (!commit) {
	//			    	//Bug: Cancelled events lose their ids
	//			    	event.setCancelled(true); //Cancel Appointment update
	//			    	calendar.doLayout();
	//			    }
	//			    else{
				    	updateEvent(event.getTarget());
	//			    }
				  }
			});
		}//End if statement for adding handlers only for admins
		else{
			//Disable drag and drop for volunteers
			settings.setEnableDragDrop(false);
		}
		calendar.addDeleteHandler(new DeleteHandler<Appointment>(){

			@Override
			public void onDelete(DeleteEvent<Appointment> event) {
				// Cancel deletion; Events cannot be deleted from the calendar
				event.setCancelled(true);
			}
			                    
		});
		
		

		
	}
	@SuppressWarnings("deprecation")
	private void updateMonthLabel(){
		String month;
		switch(calendar.getDate().getMonth()){
		case(0):
			month = "January";
			break;
		case(1):
			month = "February";
			break;
		case(2):
			month = "March";
			break;
		case(3):
			month = "April";
			break;
		case(4):
			month = "May";
			break;
		case(5):
			month = "June";
			break;
		case(6):
			month = "July";
			break;
		case(7):
			month = "August";
			break;
		case(8):
			month = "September";
			break;
		case(9):
			month = "October";
			break;
		case(10):
			month = "November";
			break;
		case(11):
			month = "December";
			break;
		default:
			month = "";
		}
		monthLabel.setText(month);
	}
	private void updateCalendar(){
		calendar.clearAppointments();
		//This is the person whose calendar to display
		Person p = Server.currentUser;
		
		if(!(p instanceof Administrator)){
			viewAllEvents = false;
		}
		
		if(viewAllEvents == false){
			//Show events for current user only
			databaseHandlerService.getEvents(p, new AsyncCallback<Vector<Event>>() {
				
				@Override
				public void onSuccess(Vector<Event> result) {
					
					//Suspend updating of the calendar while events are added
					calendar.suspendLayout();
					
					//Add all events for this person from the database
					for(int i = 0; i < result.size(); i++){
						Appointment appt = new Appointment();
						appt.setId(Integer.toString(result.get(i).getId()));
						appt.setStart(result.get(i).getStartTime());
						appt.setEnd(result.get(i).getEndTime());
						appt.setTitle(result.get(i).getName());
						appt.setDescription(result.get(i).getDescription());
						appt.setStyle(AppointmentStyle.BLUE);
						calendar.addAppointment(appt);
					}
					
					//Resume updating of the calendar
					calendar.resumeLayout();
				}	
				
				@Override
				public void onFailure(Throwable caught) {
				
				}
			});
		}
		else{
				databaseHandlerService.getAllEvents(new AsyncCallback<Vector<Event>>() {
				
				@Override
				public void onSuccess(Vector<Event> result) {
					
					//Suspend updating of the calendar while events are added
					calendar.suspendLayout();
					
					//Add all events for this person from the database
					for(int i = 0; i < result.size(); i++){
						Appointment appt = new Appointment();
						appt.setId(Integer.toString(result.get(i).getId()));
						appt.setStart(result.get(i).getStartTime());
						appt.setEnd(result.get(i).getEndTime());
						appt.setTitle(result.get(i).getName());
						appt.setDescription(result.get(i).getDescription());
						appt.setStyle(AppointmentStyle.BLUE);
						calendar.addAppointment(appt);
					}
					
					//Resume updating of the calendar
					calendar.resumeLayout();
				}	
				
				@Override
				public void onFailure(Throwable caught) {
				
				}
			});
		}
	}
	private void updateEvent (Appointment event){
		//TODO write event to database
    	calendar.doLayout();
		
		//Need to store a global variable to use event with the async callback
		tempEvent = event;
		
		//First, get the event with the same id from the database
		databaseHandlerService.getEvent(Integer.parseInt(event.getId()), new AsyncCallback<Event>() {

			@Override
			public void onFailure(Throwable caught) {
				Window.alert("Error: Database Failure.");
			}

			@Override
			public void onSuccess(Event result) {
				if(result != null){
					//Update event info
					result.setName(tempEvent.getTitle());
					result.setStartTime(tempEvent.getStart());
					result.setEndTime(tempEvent.getEnd());
				}
				//Write the updated event to the database
				writeEvent(result);			
			}
		});		
	}
	private void writeEvent(Event e){
		databaseHandlerService.write(e, new AsyncCallback<Integer>() {

			@Override
			public void onFailure(Throwable caught) {
				Window.alert("Error: Database Failure.");
			}

			@Override
			public void onSuccess(Integer result) {
				//Window.alert("Successfully wrote event to database");	
			}
		});	
	}
	
}
